#include "wifi_connect.hpp"
#include <string.h>
#include <stdlib.h>
#include "stimer.h"
#include "bsp_wifi.h"
// only debug
#include "bsp_uart.h"
#define debug_printf(fmt, ...) UARTx_Printf(&huart3, fmt, ##__VA_ARGS__)
#define debug_printn(data, n) UARTx_Printn(&huart3, data, n)

/**
 * @brief Get the broadcast ip object
 * @param ip gateway ip (7-15 byte): 0.0.0.0 - 255.255.255.255
 * @param result char[16]
 * @retval int -1:error / 0:OK
 */
int get_broadcast_ip(const char *ip, char *result)
{
    int i;
    int dotCnt = 0;
    int lastDotIndex = 0;
    for (i = 0; i < 15; i++)
    {
        if (ip[i] == '.')
        {
            dotCnt++;
            if (dotCnt == 3)
            {
                lastDotIndex = i;
            }
        }
    }
    if (dotCnt != 3 || lastDotIndex + 4 > 16)
    {
        return -1;
    }
    memcpy(result, ip, lastDotIndex + 1); // aaa.bbb.ccc.
    result[lastDotIndex + 1] = '2';       // aaa.bbb.ccc.2
    result[lastDotIndex + 2] = '5';       // aaa.bbb.ccc.25
    result[lastDotIndex + 3] = '5';       // aaa.bbb.ccc.255
    result[lastDotIndex + 4] = '\0';      // aaa.bbb.ccc.255\0
    return 0;
}

wifiConnect::wifiConnect(esp_01s_mqtt_t *wifiModule, esp_01s_mqtt_callback_t msg_callback)
{
    wifi = wifiModule;
    memset(this->wifi->ip_gateway, 0, sizeof(this->wifi->ip_gateway));
    ap_ssid = NULL;
    ap_pwd = NULL;
    ap_ssid = NULL;
    state = STATE_IDLE;
    pcallback = msg_callback;
    timeout = 60 * 1E3;
}

// /**
//  * @brief 配置SoftAP (创建热点)
//  * @param ssid
//  * @param password
//  * @param ch 信道[1 , 13]
//  * @param ecn 加密方式[0: OPEN/ 2: WPA_PSK/ 3: WPA2_PSK/ 4: WPA_WPA2_PSK]
//  */
// void wifiConnect::sap_config(const char *ssid, const char *password, uint8_t ch, uint8_t ecn)
// {
//     this->sap_ssid = (char*)ssid;
//     this->sap_password = (char*)password;
//     this->sap_ch = ch;
//     this->sap_ecn = ecn;
// }
int wifiConnect::setAP(const char *ssid, const char *pwd)
{
    if (ssid == NULL || pwd == NULL)
    {
        return -1;
    }

    ap_ssid = ssid;
    ap_pwd = pwd;
    ap_bssid = NULL;
    return 0;
}
int wifiConnect::setAP(const char *ssid, const char *pwd, const char *bssid)
{
    if (ssid == NULL || pwd == NULL)
    {
        return -1;
    }
    ap_ssid = ssid;
    ap_pwd = pwd;
    ap_bssid = bssid;
    return 0;
}

void wifiConnect::setTimeout(uint32_t time)
{
    timeout = time;
}

void wifiConnect::start_join_ap(void)
{
    this->time = 0;
    this->sleep_time = 0;
    this->sub_state = 0;
    this->sub_state_shadow = 0;
    this->state = STATE_INIT;
    esp01s_set_passthrough(wifi, 0); // 退出透传
    sleep(500, 0);
}

void wifiConnect::start_join_ap(const char *ssid, const char *password)
{
    setAP(ssid, password, NULL);
    start_join_ap();
}

void wifiConnect::start_join_ap(const char *ssid, const char *password, const char *bssid)
{
    setAP(ssid, password, bssid);
    start_join_ap();
}

void wifiConnect::stop_join_ap(void)
{
    this->state = STATE_STOP;
    awake(0);
}

bool wifiConnect::get_state(void)
{
    return this->state == STATE_CPLT;
}

void wifiConnect::run_join_ap(void)
{
    // time++;
    switch (this->state)
    {
    case STATE_INIT: // 尝试连接WIFI

        if (sub_state == 0)
        {
            // 切换为STA模式,开启自动连接
            esp01s_set_wirless_mode(wifi, 1, 2);
            sleep(300, 1);
        }
        else if (sub_state == 1)
        {
            if (esp01s_request_is_cplt(wifi))
            {
                // 切换为STA模式成功
                sleep(1, 2);
            }
            else
            {
                if (timeout < time)
                {
                    stop_join_ap();
                    break;
                }
                sleep(1, 0);
            }
        }
        else if (sub_state == 2)
        {

            // 尝试连接WIFI
            debug_printf("[wifiConnect] try connect Wifi:%s,%s\n\r", ap_ssid, ap_pwd);
            esp01s_join_ap(wifi,
                           ap_ssid,
                           ap_pwd,
                           ap_bssid);
            sleep(1000, 3);
            time = 0;
        }
        else if (sub_state == 3)
        {
            if (wifi->request_state == ESP_01S_REQ_CPLT_STATE)
            {
                // 连接完成
                debug_printf("[wifiConnect] connect OK\n\r");
                esp01s_request_is_cplt(wifi);
                state = STATE_TRY_CONNECT;
                sleep(1, 0);
            }
            else if (wifi->request_state == ESP_01S_REQ_JAP_STATE)
            {
                // 等待回复
                sleep(100);
            }
            else
            {
                // 遇到了错误，重新尝试连接
                esp01s_request_set_idle(wifi);
                sleep(1, 0);
            }
        }
        break;
    case STATE_TRY_CONNECT: // 发送申请报文1后等待回复
        if (sub_state == 0)
        {
            // 请求获取网关ip 得到广播地址
            debug_printf("[wifiConnect] request ip...\n\r");
            esp01s_get_ip_state_request(wifi);
            sleep(500, 1);
        }
        else if (sub_state == 1)
        {
            if (esp01s_request_is_cplt(wifi))
            {
                debug_printf("[wifiConnect] get ip\n\r");
                // 获取网关ip成功
                sleep(500, 2);
            }
            else
            {
                sleep(1, 0); // 重新获取
            }
        }
        else if (sub_state == 2)
        {
            uint8_t ip_len = strlen(wifi->ip_gateway);
            if (ip_len >= 7)
            {
                esp01s_request_set_idle(wifi); // 清空请求状态
                // 计算广播地址
                char ip[16] = {0};
                int state = get_broadcast_ip(this->wifi->ip_gateway, ip);
                if (ip[0] == '0' || state == -1)
                {
                    // 没有连接上wifi，返回上个状态重新连接
                    state = STATE_INIT;
                    sleep(1, 0);
                }
                // 创建UDP连接
                debug_printf("[wifiConnect] try to connect host:%s\n\r", ip);
                esp01s_connect(wifi, 1, ip, 8888, 8888, pcallback);
                sleep(500, 3);
            }
        }
        else if (sub_state == 3)
        {
            if (esp01s_request_is_cplt(wifi))
            {
                esp01s_set_transmit_mode(wifi, 1);
                sleep(200, 4);
            }
            else
            {
                // 连接失败重新请求
                sleep(1, 2);
            }
        }
        else if (sub_state == 4)
        {
            if (esp01s_request_is_cplt(wifi))
            {
                esp01s_set_passthrough(wifi, 1); // 开启数据透传
                sleep(300, 5);
            }
            else
            {
                esp01s_set_transmit_mode(wifi, 1);
                sleep(400, 4);
            }
        }
        else if (sub_state == 5)
        {
            if (esp01s_request_is_cplt(wifi))
            {
                debug_printf("[wifiConnect] UDP connect OK\n\r");
                Beep(300);
                state = STATE_CPLT;
                sleep(1, 0);
            }
            else // 透传失败，重新连接
            {
                sleep(1, 2);
            }
        }
        break;
    case STATE_CPLT: // 完成连接

        break;
    case STATE_STOP:
        // TODO:
        if (sub_state == 0)
        {
            esp01s_set_passthrough(wifi, 0); // 退出透传
            sleep(500, 1);
			return;
        }
        else if (sub_state == 1)
        {
            esp01s_reset(wifi);
            state = STATE_IDLE;
            time = 0;
            return;
        }
        break;
    default:
        break;
    }

    if (STATE_IDLE != state && time > timeout)
    {
        stop_join_ap();
        debug_printf("[wifi] Connect timeout,stop connect\n\r");
        return;
    }
}

void wifiConnect::wake_task(const void *arg)
{
    wifiConnect *wifiCfg = (wifiConnect *)arg;
    wifiCfg->time += wifiCfg->sleep_time;
    wifiCfg->sleep_time = 0;
    if (wifiCfg->sub_state_shadow != 0xFF)
    {
        wifiCfg->sub_state = wifiCfg->sub_state_shadow;
    }
    wifiCfg->sub_state_shadow = 0;
    // debug_printf("wake up wifi connect task\n\r");
    wifiCfg->run_join_ap();
}

/**
 * @brief 休眠一段时间
 * @param ms 时间
 * @param subState 休眠后更新子状态的值[0,255)
 * @retval true 休眠成功
 * @retval false 当前休眠未结束
 */
bool wifiConnect::sleep(uint32_t ms, uint8_t subState)
{
    if (sleep_time != 0)
    {
        return 0; // 当前休眠未结束
    }
    if (ms == 0)
        ms = 1;
    sleep_time = ms; // 开启休眠
    sub_state_shadow = subState;
    stimer_task_oneshot(wake_task, sleep_time, 1, this);
    return 1;
}

bool wifiConnect::sleep(uint32_t ms)
{
    return sleep(ms, 0xFF); // 0xFF用做不更新子状态设置的标志
}

bool wifiConnect::awake(void)
{
    // debug_printf("currnt state:%d,next:%d\n\r", state, sub_state);
    sleep_time = 0;
    return sleep(1);
}

bool wifiConnect::awake(uint8_t substate)
{
    // debug_printf("currnt state:%d,next:%d\n\r", state, substate);
    sleep_time = 0;
    return sleep(1, substate);
}
